home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 21 / AACD 21.iso / AACD / Utilities / Ghostscript / src / gxhint3.c < prev    next >
Encoding:
C/C++ Source or Header  |  2001-01-01  |  17.2 KB  |  557 lines

  1. /* Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999 Aladdin Enterprises.  All rights reserved.
  2.   
  3.   This file is part of AFPL Ghostscript.
  4.   
  5.   AFPL Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author or
  6.   distributor accepts any responsibility for the consequences of using it, or
  7.   for whether it serves any particular purpose or works at all, unless he or
  8.   she says so in writing.  Refer to the Aladdin Free Public License (the
  9.   "License") for full details.
  10.   
  11.   Every copy of AFPL Ghostscript must include a copy of the License, normally
  12.   in a plain ASCII text file named PUBLIC.  The License grants you the right
  13.   to copy, modify and redistribute AFPL Ghostscript, but only under certain
  14.   conditions described in the License.  Among other things, the License
  15.   requires that the copyright notice and this notice be preserved on all
  16.   copies.
  17. */
  18.  
  19. /*$Id: gxhint3.c,v 1.2 2000/09/19 19:00:37 lpd Exp $ */
  20. /* Apply hints for Type 1 fonts. */
  21. #include "gx.h"
  22. #include "gserrors.h"
  23. #include "gxarith.h"
  24. #include "gxfixed.h"
  25. #include "gxmatrix.h"
  26. #include "gxfont.h"
  27. #include "gxfont1.h"
  28. #include "gxtype1.h"
  29. #include "gzpath.h"
  30.  
  31. /* ------ Path hints ------ */
  32.  
  33. /* Forward references */
  34. private void
  35.      apply_hstem_hints(P3(gs_type1_state *, int, gs_fixed_point *)), apply_vstem_hints(P3(gs_type1_state *, int, gs_fixed_point *));
  36.  
  37.  
  38. /*
  39.  * Apply hints along a newly added tail of a subpath.
  40.  * Path segments require hints as follows:
  41.  *      Nearly vertical line: vstem hints at both ends.
  42.  *      Nearly horizontal line: hstem hints at both ends.
  43.  *      Curve with nearly vertical/horizontal start/end:
  44.  *        vstem/hstem hints at start/end.
  45.  * We also must take care to handle the implicit closing line for
  46.  * subpaths that aren't explicitly closed.
  47.  *
  48.  * Note that "upper" and "lower" refer to device coordinates, which are
  49.  * what we use throughout the Type 1 code; however, "horizontal" and
  50.  * "vertical" refer to the character space coordinate system.
  51.  */
  52. #define HINT_VERT_LOWER 1
  53. #define HINT_VERT_UPPER 2    /* must be > lower */
  54. #define HINT_VERT (HINT_VERT_LOWER | HINT_VERT_UPPER)
  55. #define HINT_HORZ_LOWER 4
  56. #define HINT_HORZ_UPPER 8    /* must be > lower */
  57. #define HINT_HORZ (HINT_HORZ_LOWER | HINT_HORZ_UPPER)
  58. private inline bool
  59. nearly_axial(fixed dmajor, fixed dminor)
  60. {
  61.     return (dminor <= dmajor >> 4);
  62. }
  63.  
  64. /*
  65.  * Determine which types of hints, if any, are applicable to a given
  66.  * line segment.
  67.  */
  68. private int
  69. line_hints(const gs_type1_state * pcis, const gs_fixed_point * p0,
  70.        const gs_fixed_point * p1)
  71. {
  72.     fixed dx = p1->x - p0->x;
  73.     fixed dy = p1->y - p0->y;
  74.     fixed adx, ady;
  75.     bool xi = pcis->fh.x_inverted, yi = pcis->fh.y_inverted;
  76.     int hints;
  77.  
  78.     /*
  79.      * To figure out which side of the stem we are on, we assume that the
  80.      * inside of the filled area is always to the left of the edge, i.e.,
  81.      * edges moving in -X or +Y in character space are on the "upper" side
  82.      * of the stem, while edges moving by +X or -Y are on the "lower" side.
  83.      * (See section 3.5 of the Adobe Type 1 Font Format book.)
  84.      */
  85.  
  86.     /*
  87.      * Map the deltas back into character space.  This is essentially an
  88.      * inverse-distance-transform with the combined matrix, but we don't
  89.      * bother to undo the scaling, since it only matters for the axiality
  90.      * test and we don't care about situations where X and Y scaling are
  91.      * radically different.
  92.      */
  93.     if (xi)
  94.     dx = -dx;
  95.     if (yi)
  96.     dy = -dy;
  97.     if (pcis->fh.axes_swapped) {
  98.     fixed t = dx;
  99.     int ti = xi;
  100.  
  101.     dx = dy, xi = yi;
  102.     dy = t, yi = ti;
  103.     }
  104.     adx = any_abs(dx);
  105.     ady = any_abs(dy);
  106.     /*
  107.      * Note that since upper/lower refer to device space, we must
  108.      * interchange them if the corresponding axis is inverted.
  109.      */
  110.     if (dy != 0 && nearly_axial(ady, adx)) {
  111.     hints = (dy > 0 ? HINT_VERT_UPPER : HINT_VERT_LOWER);
  112.     if (xi)
  113.         hints ^= (HINT_VERT_LOWER | HINT_VERT_UPPER);
  114.     } else if (dx != 0 && nearly_axial(adx, ady)) {
  115.     hints = (dx < 0 ? HINT_HORZ_UPPER : HINT_HORZ_LOWER);
  116.     if (yi)
  117.         hints ^= (HINT_HORZ_LOWER | HINT_HORZ_UPPER);
  118.     } else
  119.     hints = 0;
  120.     if_debug7('y', "[y]hint from 0x%lx(%1.4f,%1.4f) to 0x%lx(%1.4f,%1.4f) = %d\n",
  121.           (ulong) p0, fixed2float(p0->x), fixed2float(p0->y),
  122.           (ulong) p1, fixed2float(p1->x), fixed2float(p1->y),
  123.           hints);
  124.     return hints;
  125. }
  126.  
  127. /* Apply hints at a point.  Optionally return the amount of adjustment. */
  128. private void
  129. apply_hints_at(gs_type1_state * pcis, int hints, gs_fixed_point * ppt,
  130.            gs_fixed_point * pdiff)
  131. {
  132.     fixed px = ppt->x, py = ppt->y;
  133.  
  134.     if_debug4('y', "[y]applying hints %d to 0x%lx(%1.4f,%1.4f) ...\n",
  135.           hints, (ulong) ppt, fixed2float(px), fixed2float(py));
  136.     if ((hints & HINT_VERT) != 0 &&
  137.     (pcis->vstem_hints.count & pcis->dotsection_flag) != 0
  138.     )
  139.     apply_vstem_hints(pcis, (hints & HINT_VERT_UPPER) -
  140.               (hints & HINT_VERT_LOWER), ppt);
  141.     if ((hints & HINT_HORZ) != 0 &&
  142.     (pcis->hstem_hints.count & pcis->dotsection_flag) != 0
  143.     )
  144.     apply_hstem_hints(pcis, (hints & HINT_HORZ_UPPER) -
  145.               (hints & HINT_HORZ_LOWER), ppt);
  146.     if (pdiff != NULL)
  147.     pdiff->x = ppt->x - px,
  148.         pdiff->y = ppt->y - py;
  149.     /* Here is where we would round *ppt to the nearest quarter-pixel */
  150.     /* if we wanted to. */
  151.     if_debug2('y', "[y] ... => (%1.4f,%1.4f)\n",
  152.           fixed2float(ppt->x), fixed2float(ppt->y));
  153. }
  154.  
  155. /* Add a hint delta to a point. */
  156. #ifndef DEBUG
  157. inline
  158. #endif
  159. private void
  160. add_hint_diff(gs_fixed_point * ppt, gs_fixed_point delta)
  161. {
  162.     if_debug7('y', "[y]adding diff (%1.4f,%1.4f) to 0x%lx(%1.4f,%1.4f) => (%1.4f,%1.4f)\n",
  163.           fixed2float(delta.x), fixed2float(delta.y), (ulong) ppt,
  164.           fixed2float(ppt->x), fixed2float(ppt->y),
  165.           fixed2float(ppt->x + delta.x), fixed2float(ppt->y + delta.y));
  166.     ppt->x += delta.x;
  167.     ppt->y += delta.y;
  168. }
  169.  
  170. /* Test whether a line is null. */
  171. inline private bool
  172. line_is_null(gs_fixed_point p0, gs_fixed_point p1)
  173. {
  174.     return (any_abs(p1.x - p0.x) + any_abs(p1.y - p0.y) < fixed_epsilon * 4);
  175. }
  176.  
  177. /*
  178.  * Adjust the other control points of a curve proportionately when moving
  179.  * one end.  The Boolean argument indicates whether the point being
  180.  * adjusted is the one nearer the point that was moved.
  181.  */
  182. private fixed
  183. scale_delta(fixed diff, fixed dv, fixed lv, bool nearer)
  184. {
  185.     if (dv == 0)
  186.     return 0;
  187.     /*
  188.      * fixed_mult_quo requires non-negative 2nd and 3rd arguments,
  189.      * and also 2nd argument < 3rd argument.
  190.      * If it weren't for that, we would just use it directly.
  191.      *
  192.      * lv = 0 is implausible, but we have to allow for it.
  193.      */
  194.     if (lv == 0)
  195.     return (nearer ? diff : fixed_0);
  196.     if (lv < 0)
  197.     lv = -lv, dv = -dv;
  198.     if (dv < 0)
  199.     dv = -dv, diff = -diff;
  200.     /*
  201.      * If dv > lv, there has been some kind of anomaly similar to
  202.      * the lv = 0 case.
  203.      */
  204.     if (dv >= lv)
  205.     return (nearer ? diff : fixed_0);
  206.     else
  207.     return fixed_mult_quo(diff, dv, lv);
  208. }
  209. private void
  210. adjust_curve_start(curve_segment * pcseg, const gs_fixed_point * pdiff)
  211. {
  212.     fixed dx = pdiff->x, dy = pdiff->y;
  213.     fixed end_x = pcseg->pt.x, end_y = pcseg->pt.y;
  214.     const segment *prev = pcseg->prev;
  215.     fixed lx = end_x - (prev->pt.x - dx), ly = end_y - (prev->pt.y - dy);
  216.     gs_fixed_point delta;
  217.  
  218.     delta.x = scale_delta(dx, end_x - pcseg->p1.x, lx, true);
  219.     delta.y = scale_delta(dy, end_y - pcseg->p1.y, ly, true);
  220.     add_hint_diff(&pcseg->p1, delta);
  221.     delta.x = scale_delta(dx, end_x - pcseg->p2.x, lx, false);
  222.     delta.y = scale_delta(dy, end_y - pcseg->p2.y, ly, false);
  223.     add_hint_diff(&pcseg->p2, delta);
  224. }
  225. private void
  226. adjust_curve_end(curve_segment * pcseg, const gs_fixed_point * pdiff)
  227. {
  228.     fixed dx = pdiff->x, dy = pdiff->y;
  229.     const segment *prev = pcseg->prev;
  230.     fixed start_x = prev->pt.x, start_y = prev->pt.y;
  231.     fixed lx = pcseg->pt.x - dx - start_x, ly = pcseg->pt.y - dy - start_y;
  232.     gs_fixed_point delta;
  233.  
  234.     delta.x = scale_delta(dx, pcseg->p1.x - start_x, lx, false);
  235.     delta.y = scale_delta(dy, pcseg->p1.y - start_y, ly, false);
  236.     add_hint_diff(&pcseg->p1, delta);
  237.     delta.x = scale_delta(dx, pcseg->p2.x - start_x, lx, true);
  238.     delta.y = scale_delta(dy, pcseg->p2.y - start_y, ly, true);
  239.     add_hint_diff(&pcseg->p2, delta);
  240. }
  241.  
  242. /*
  243.  * Propagate a final wraparound hint back through any null line segments
  244.  * to a possible curve.  pseg_last.pt has already been adjusted.
  245.  */
  246. private void
  247. apply_final_hint(segment * pseg_last, const gs_fixed_point * pdiff)
  248. {
  249.     segment *pseg;
  250.  
  251.     for (pseg = pseg_last;; pseg = pseg->prev) {
  252.     segment *prev = pseg->prev;
  253.  
  254.     switch (pseg->type) {
  255.         case s_curve:
  256.         adjust_curve_end(((curve_segment *) pseg), pdiff);
  257.         return;
  258.         case s_line:
  259.         case s_line_close:
  260.         if (!line_is_null(prev->pt, pseg->pt))
  261.             return;
  262.         add_hint_diff(&prev->pt, *pdiff);
  263.         break;
  264.         default:        /* s_start */
  265.         return;
  266.     }
  267.     }
  268. }
  269.  
  270. /*
  271.  * Handle the end of the subpath wrapping around to the start.  This is
  272.  * ugly, messy code that we should be able to improve, but I neither see how
  273.  * to do it nor understand how the IBM Type 1 rasterizer can produce such
  274.  * good results without doing anything like this.
  275.  *
  276.  * This is a separate procedure only for readability: it is only called
  277.  * from one place in the next procedure.
  278.  */
  279. private void
  280. apply_wrapped_hints(gs_type1_state * pcis, subpath * psub, segment * pseg,
  281.             int hints, gs_fixed_point * pdiff)
  282. {
  283.     /* Some fonts don't use closepath when they should.... */
  284.     fixed ctemp;
  285.     bool closed =
  286.     (pseg->type == s_line_close ||
  287.      ((ctemp = pseg->pt.x - psub->pt.x,
  288.        any_abs(ctemp) < float2fixed(0.1)) &&
  289.       (ctemp = pseg->pt.y - psub->pt.y,
  290.        any_abs(ctemp) < float2fixed(0.1))));
  291.     segment *const pfirst = psub->next;
  292.     int hints_first = pcis->hints_initial;
  293.  
  294.     if (closed) {
  295.     /*
  296.      * Apply the union of the hints at both the end (pseg) and the start
  297.      * (psub) of the subpath.  Note that we have already applied hints
  298.      * at the end, and hints_first at the start.  However, because of
  299.      * hint replacement, the points might differ even if hints ==
  300.      * hints_first.  In this case, the initial hints take priority,
  301.      * because the initial segment was laid down first.
  302.      */
  303.     int do_x, do_y;
  304.     gs_fixed_point diff2;
  305.  
  306.     if_debug2('y', "[y]closing closed, hints=%d, hints_first=%d\n",
  307.           hints, hints_first);
  308.     if (pcis->fh.axes_swapped)
  309.         do_x = HINT_HORZ, do_y = HINT_VERT;
  310.     else
  311.         do_x = HINT_VERT, do_y = HINT_HORZ;
  312.     {
  313.         /* Apply hints_first - hints to the end. */
  314.         int hints_end = hints_first & ~hints;
  315.  
  316.         diff2.x =
  317.         (hints_end & do_x ?
  318.          psub->pt.x - pcis->unmoved_start.x : 0);
  319.         diff2.y =
  320.         (hints_end & do_y ?
  321.          psub->pt.y - pcis->unmoved_start.y : 0);
  322.     }
  323.     {
  324.         /* Apply hints - hints_first to the start. */
  325.         int hints_start = hints & ~hints_first;
  326.  
  327.         pdiff->x =
  328.         (hints_start & do_x ?
  329.          pseg->pt.x - pcis->unmoved_end.x : 0);
  330.         pdiff->y =
  331.         (hints_start & do_y ?
  332.          pseg->pt.y - pcis->unmoved_end.y : 0);
  333.     }
  334.     add_hint_diff(&pseg->pt, diff2);
  335.     apply_final_hint(pseg, &diff2);
  336.     add_hint_diff(&psub->pt, *pdiff);
  337.     /*
  338.      * Now align the initial and final points, to deal with hint
  339.      * replacement.
  340.      */
  341.     diff2.x = psub->pt.x - pseg->pt.x;
  342.     diff2.y = psub->pt.y - pseg->pt.y;
  343.     if (diff2.x || diff2.y) {
  344.         /* Force the points to coincide. */
  345.         pseg->pt = psub->pt;
  346.         apply_final_hint(pseg, &diff2);
  347.     }
  348.     } else {
  349.     int hints_close =
  350.     line_hints(pcis, &pcis->unmoved_end, &pcis->unmoved_start);
  351.  
  352.     hints_close &= ~(hints | hints_first);
  353.     if_debug3('y', "[y]closing open, hints=%d, hints_close=%d, hints_first=%d\n",
  354.           hints, hints_close, hints_first);
  355.     if (hints_close) {
  356.         apply_hints_at(pcis, hints_close, &pseg->pt, pdiff);
  357.         apply_final_hint(pseg, pdiff);
  358.         apply_hints_at(pcis, hints_close, &psub->pt, pdiff);
  359.     } else
  360.         pdiff->x = pdiff->y = 0;
  361.     }
  362.     if (pfirst->type == s_curve)
  363.     adjust_curve_start((curve_segment *) pfirst, pdiff);
  364. }
  365.  
  366. /*
  367.  * Apply hints along a subpath.  If closing is true, consider the subpath
  368.  * closed; if not, we may add more to the subpath later.  In the latter case,
  369.  * don't do anything if the subpath is closed, because we already applied
  370.  * the hints.
  371.  */
  372. void
  373. type1_apply_path_hints(gs_type1_state * pcis, bool closing, gx_path * ppath)
  374. {
  375.     segment *pseg = pcis->hint_next;
  376.     segment *pnext;
  377.     subpath *const psub = ppath->current_subpath;
  378.  
  379.     /*
  380.      * hints holds the set of hints that have already been applied (if
  381.      * applicable) to pseg->pt, and hence should not be applied again.
  382.      */
  383.     int hints = pcis->hints_pending;
  384.     gs_fixed_point diff;
  385.  
  386.     /*
  387.      * Since unknown OtherSubrs call apply_path_hints before returning
  388.      * to the client, and since OtherSubrs may be invoked before the
  389.      * [h]sbw is seen, it's possible that init_done < 0, i.e., the path
  390.      * and hint members of the state haven't been set up yet.  In this
  391.      * case, we know there are no relevant hints.
  392.      */
  393.     if (pcis->init_done < 0)
  394.     return;
  395.     if (pseg == 0) {
  396.     /* Start at the beginning of the subpath. */
  397.     if (psub == 0)
  398.         return;
  399.     if (psub->is_closed && !closing)
  400.         return;
  401.     pseg = (segment *) psub;
  402.     if (pseg->next == 0)
  403.         return;
  404.     hints = 0;
  405.     pcis->unmoved_start = psub->pt;
  406.     pcis->unmoved_end = psub->pt;
  407.     } else
  408.     hints = pcis->hints_pending;
  409.     diff.x = diff.y = 0;
  410.     for (; (pnext = pseg->next) != 0; pseg = pnext) {
  411.     int hints_next;
  412.  
  413.     /*
  414.      * Apply hints to the end of the previous segment (pseg)
  415.      * and the beginning of this one (pnext).
  416.      */
  417.     gs_fixed_point dseg;
  418.  
  419.     switch (pnext->type) {
  420.         case s_curve:{
  421.             curve_segment *const pnext_curve = (curve_segment *) pnext;
  422.             int hints_first =
  423.             line_hints(pcis, &pcis->unmoved_end,
  424.                    &pnext_curve->p1) & ~hints;
  425.             gs_fixed_point diff2;
  426.  
  427.             if (pseg == (segment *) psub)
  428.             pcis->hints_initial = hints_first;
  429.             if (hints_first)
  430.             apply_hints_at(pcis, hints_first, &pseg->pt, &dseg);
  431.             else
  432.             dseg.x = dseg.y = 0;
  433.             diff2.x = pseg->pt.x - pcis->unmoved_end.x;
  434.             diff2.y = pseg->pt.y - pcis->unmoved_end.y;
  435.             hints_next = line_hints(pcis, &pnext_curve->p2, &pnext->pt);
  436.             adjust_curve_start(pnext_curve, &diff2);
  437.             if (hints_next) {
  438.             apply_hints_at(pcis, hints_next, &pnext_curve->p2, &diff);
  439.             pcis->unmoved_end = pnext->pt;
  440.             add_hint_diff(&pnext->pt, diff);
  441.             } else
  442.             pcis->unmoved_end = pnext->pt;
  443.             break;
  444.         }
  445.         case s_line_close:
  446.         /* Undo any initial hints propagated to the end. */
  447.         pnext->pt = pcis->unmoved_start;
  448.         default:        /* s_line, s_line_close */
  449.         if (line_is_null(pnext->pt, pcis->unmoved_end)) {
  450.             /* This is a null line, just move it. */
  451.             hints_next = hints;
  452.             dseg.x = dseg.y = 0;    /* don't move p2 again */
  453.         } else {
  454.             hints_next =
  455.             line_hints(pcis, &pcis->unmoved_end, &pnext->pt);
  456.             if (hints_next & ~hints)
  457.             apply_hints_at(pcis, hints_next & ~hints,
  458.                        &pseg->pt, &dseg);
  459.             else
  460.             dseg.x = dseg.y = 0;
  461.         }
  462.         if (pseg == (segment *) psub)
  463.             pcis->hints_initial = hints_next;
  464.         pcis->unmoved_end = pnext->pt;
  465.         if (hints_next)
  466.             apply_hints_at(pcis, hints_next, &pnext->pt, NULL);
  467.     }
  468.     if (pseg->type == s_curve)
  469.         adjust_curve_end((curve_segment *) pseg, &dseg);
  470.     hints = hints_next;
  471.     }
  472.     if (closing) {
  473.     apply_wrapped_hints(pcis, psub, pseg, hints, &diff);
  474.     pcis->hint_next = 0;
  475.     pcis->hints_pending = 0;
  476.     } else {
  477.     pcis->hint_next = pseg;
  478.     pcis->hints_pending = hints;
  479.     }
  480. }
  481.  
  482. /* ------ Individual hints ------ */
  483.  
  484. private const stem_hint *search_hints(P2(stem_hint_table *, fixed));
  485.  
  486. /*
  487.  * Adjust a point according to the relevant hints.
  488.  * dx or dy is > 0 for the upper edge, < 0 for the lower.
  489.  * The caller is responsible for checking use_hstem_hints or use_vstem_hints
  490.  * and not calling the find_xxx_hints routine if this is false.
  491.  * Note that if use_x/y_hints is false, no entries ever get made
  492.  * in the stem hint tables, so these routines will not get called.
  493.  */
  494.  
  495. private void
  496. apply_vstem_hints(gs_type1_state * pcis, int dy, gs_fixed_point * ppt)
  497. {
  498.     fixed *pv = (pcis->fh.axes_swapped ? &ppt->y : &ppt->x);
  499.     const stem_hint *ph = search_hints(&pcis->vstem_hints, *pv);
  500.  
  501.     if (ph != 0) {
  502.     if_debug3('Y', "[Y]use vstem %d: %1.4f (%s)",
  503.           (int)(ph - &pcis->vstem_hints.data[0]),
  504.           fixed2float(*pv),
  505.           (dy == 0 ? "?!" : dy > 0 ? "upper" : "lower"));
  506. #ifdef DEBUG
  507.     if (dy == 0) {
  508.         lprintf("dy == 0 in apply_vstem_hints!\n");
  509.         return;
  510.     }
  511. #endif
  512.     *pv += (dy > 0 ? ph->dv1 : ph->dv0);
  513.     if_debug1('Y', " -> %1.4f\n", fixed2float(*pv));
  514.     }
  515. }
  516.  
  517. private void
  518. apply_hstem_hints(gs_type1_state * pcis, int dx, gs_fixed_point * ppt)
  519. {
  520.     fixed *pv = (pcis->fh.axes_swapped ? &ppt->x : &ppt->y);
  521.     const stem_hint *ph = search_hints(&pcis->hstem_hints, *pv);
  522.  
  523.     if (ph != 0) {
  524.     if_debug3('Y', "[Y]use hstem %d: %1.4f (%s)",
  525.           (int)(ph - &pcis->hstem_hints.data[0]),
  526.           fixed2float(*pv),
  527.           (dx == 0 ? "?!" : dx > 0 ? "upper" : "lower"));
  528. #ifdef DEBUG
  529.     if (dx == 0) {
  530.         lprintf("dx == 0 in apply_vstem_hints!\n");
  531.         return;
  532.     }
  533. #endif
  534.     *pv += (dx > 0 ? ph->dv1 : ph->dv0);
  535.     if_debug1('Y', " -> %1.4f\n", fixed2float(*pv));
  536.     }
  537. }
  538.  
  539. /* Search one hint table for an adjustment. */
  540. private const stem_hint *
  541. search_hints(stem_hint_table * psht, fixed v)
  542. {
  543.     const stem_hint *table = &psht->data[0];
  544.     const stem_hint *ph = table + psht->current;
  545.  
  546.     if (v >= ph->v0 && v <= ph->v1 && ph->active)
  547.     return ph;
  548.     /* We don't bother with binary or even up/down search, */
  549.     /* because there won't be very many hints. */
  550.     for (ph = &table[psht->count]; --ph >= table;)
  551.     if (v >= ph->v0 && v <= ph->v1 && ph->active) {
  552.         psht->current = ph - table;
  553.         return ph;
  554.     }
  555.     return 0;
  556. }
  557.